﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Silverlight.Validators.Controls;

namespace Silverlight.Validators
{
	public enum ValidationType
	{
		Validator,
		OnDemand
	}

    
	public abstract class ValidatorBase : DependencyObject
	{
		protected ValidatorManager Manager { get; set; }
	

        public string ManagerName { get; set; }
        public ValidationType ValidationType { get; set; }
        public IIndicator Indicator { get; set; }
        public FrameworkElement ElementToValidate { get; set; }
		public bool IsRequired { get; set; }
		public bool IsValid { get; set; }
		public Brush InvalidBackground { get; set; }
		public Brush InvalidBorder { get; set; }
		public Thickness InvalidBorderThickness { get; set; }
		public string ErrorMessage { get; set; }

		private Brush OrigBackground = null;
		private Brush OrigBorder = null;
		private Thickness OrigBorderThickness = new Thickness(1);
		private object OrigTooltip = null;
		
		public ValidatorBase()
		{
			IsRequired = false;
			IsValid = true;
			ManagerName = "";
			this.ValidationType = ValidationType.Validator;
		}

		public void Initialize(FrameworkElement element)
		{
			ElementToValidate = element;
			element.Loaded += new RoutedEventHandler(element_Loaded);
		}

		private bool loaded = false;
		public UserControl UserControl { get; set; }
		private void element_Loaded(object sender, RoutedEventArgs e)
		{
			if (!loaded)
			{
				this.UserControl = FindUserControl(ElementToValidate);

				//no usercontrol.  throw error?
				if (this.UserControl == null) return;

				this.Manager = FindManager(this.UserControl, ManagerName);
				if (this.Manager == null)
				{
				    System.Diagnostics.Debug.WriteLine(String.Format("No ValidatorManager found named '{0}'", ManagerName));
				    throw new Exception(String.Format("No ValidatorManager found named '{0}'", ManagerName));
				}
				this.Manager.Register(ElementToValidate, this);

				if (ValidationType == ValidationType.Validator)
				{
					ActivateValidationRoutine();
				}

                //Use the properties from the manager if they are not set at the control level
                if (this.InvalidBackground == null)
                {
                    this.InvalidBackground = this.Manager.InvalidBackground;
                }

                if (this.InvalidBorder == null)
                {
                    this.InvalidBorder = this.Manager.InvalidBorder;

                    if (InvalidBorderThickness.Bottom == 0)
                    {
                        this.InvalidBorderThickness = this.Manager.InvalidBorderThickness;
                    }
                }

				if (this.Indicator ==null)
				{
					Type x = this.Manager.Indicator.GetType();
					this.Indicator = x.GetConstructor(System.Type.EmptyTypes).Invoke(null) as IIndicator;
					foreach (var param in x.GetProperties())
					{
						var val = param.GetValue(this.Manager.Indicator, null);
						if (param.CanWrite && val!= null && val.GetType().IsPrimitive)
						{
							param.SetValue(this.Indicator, val, null);
						}
					}
				}
				loaded = true;
			}
			ElementToValidate.Loaded -= new RoutedEventHandler(element_Loaded);
		}
		
		public void SetManagerAndControl(ValidatorManager manager, FrameworkElement element)
		{
			this.Manager = manager;
			this.ElementToValidate = element;
		}

		public bool Validate(bool checkControl)
		{
			bool newIsValid;
            if (checkControl)
            {
                newIsValid= ValidateControl() && ValidateRequired();
            }
            else
            {
                newIsValid = ValidateRequired();
            }

			if (newIsValid && !IsValid)
			{
				ControlValid();
			}
			if (!newIsValid && IsValid)
			{
				ControlNotValid();
			}
			IsValid=newIsValid;
			return IsValid;
		}
		
        public virtual void ActivateValidationRoutine()
        {
            ElementToValidate.LostFocus += new RoutedEventHandler(ElementToValidate_LostFocus);
            ElementToValidate.KeyUp += new KeyEventHandler(ElementToValidate_KeyUp);
        }

		/// <summary>
		/// Find the nearest UserControl up the control tree for the FrameworkElement passed in
		/// </summary>
		/// <param name="element">Control to validate</param>
		protected static UserControl FindUserControl(FrameworkElement element)
		{
			if (element == null)
			{
				return null;
			}
			if (element.Parent != null)
			{
				if (element.Parent is UserControl)
				{
					return element.Parent as UserControl;
				}
				return FindUserControl(element.Parent as FrameworkElement);
			}
			return null;
		}

        protected virtual void ElementToValidate_KeyUp(object sender, RoutedEventArgs e)
        {
			Dispatcher.BeginInvoke(delegate() { Validate(false); });
		}

        protected virtual void ElementToValidate_LostFocus(object sender, RoutedEventArgs e)
        {
			Dispatcher.BeginInvoke(delegate() { Validate(true); });
        }

		protected abstract bool ValidateControl();

        protected bool ValidateRequired()
        {
            if (IsRequired && ElementToValidate is TextBox)
            {
                TextBox box = ElementToValidate as TextBox;
                return !String.IsNullOrEmpty(box.Text);
            }
            return true;
        }

        protected void ControlNotValid()
        {
            GoToInvalidStyle();
        }

        protected void ControlValid()
        {
            GoToValidStyle();
        }
		
		protected virtual void GoToInvalidStyle()
        {
			if (!string.IsNullOrEmpty(this.ErrorMessage))
			{
				object tooltip = ToolTipService.GetToolTip(ElementToValidate);

				if (tooltip != null)
				{
					OrigTooltip = tooltip;
				}

				//causing a onownermouseleave error currently...
				this.ElementToValidate.ClearValue(ToolTipService.ToolTipProperty);

				SetToolTip(this.ElementToValidate, this.ErrorMessage);
			}
			
            if (Indicator != null)
            {
				Indicator.ShowIndicator(this);
            }

            if (ElementToValidate is TextBox)
            {
                TextBox box = ElementToValidate as TextBox;

                if (InvalidBackground != null)
                {
                    if (OrigBackground == null)
                    {
                    OrigBackground = box.Background;
                    }
                    box.Background = InvalidBackground;
                }

                if (InvalidBorder != null)
                {
                    if (OrigBorder == null)
                    {
                    OrigBorder = box.BorderBrush;
                        OrigBorderThickness = box.BorderThickness;
                    }
                    box.BorderBrush = InvalidBorder;

                    if (InvalidBorderThickness != null)
                    {
                        box.BorderThickness = InvalidBorderThickness;
                    }
                }
            }	
        }

		protected virtual void GoToValidStyle()
        {
            if (!string.IsNullOrEmpty(this.ErrorMessage))
            {
				this.ElementToValidate.ClearValue(ToolTipService.ToolTipProperty);

				if (this.OrigTooltip != null)
                {
					SetToolTip(this.ElementToValidate, this.OrigTooltip);
                }
            }

			if (Indicator != null)
			{
				Indicator.HideIndicator();
			}

            if (ElementToValidate is TextBox)
            {
                TextBox box = ElementToValidate as TextBox;
                if (OrigBackground != null)
                {
                    box.Background = OrigBackground;
                }

                if (OrigBorder != null)
                {
                    box.BorderBrush = OrigBorder;

                    if (OrigBorderThickness != null)
                    {
                        box.BorderThickness = OrigBorderThickness;
                    }
                }
            }
        }

		protected void SetToolTip(FrameworkElement element, object tooltip)
		{
			Dispatcher.BeginInvoke(() =>
						ToolTipService.SetToolTip(element, tooltip));
		}

		private ValidatorManager FindManager(UserControl c, string groupName)
		{
			string defaultName = "_DefaultValidatorManager";
			var mgr = this.UserControl.FindName(ManagerName);
			if (mgr == null)
			{
				mgr = this.UserControl.FindName(defaultName);
			}
			if (mgr == null)
			{
				mgr = new ValidatorManager()
				{
					Name = defaultName
				};
				Panel g = c.FindName("LayoutRoot") as Panel;
				g.Children.Add(mgr as ValidatorManager);
			}
			return mgr as ValidatorManager;
		}

	}
}